package com.sudo.service.admin.service.impl;

import com.github.pagehelper.PageHelper;
import com.sudo.common.component.RedisKeys;
import com.sudo.common.constant.UserConstant;
import com.sudo.common.enums.SysStatus;
import com.sudo.common.utils.LocalDateTimeUtil;
import com.sudo.common.utils.PagedGridResult;
import com.sudo.common.utils.RandomUtil;
import com.sudo.dao.admin.entity.SysRole;
import com.sudo.dao.admin.entity.SysRoleButton;
import com.sudo.dao.admin.entity.SysRoleMenu;
import com.sudo.dao.admin.entity.SysUserRole;
import com.sudo.dao.admin.mapper.*;
import com.sudo.dao.admin.model.bo.SysRoleBO;
import com.sudo.dao.admin.model.vo.SysRoleButtonVO;
import com.sudo.dao.admin.model.vo.SysRoleVO;
import com.sudo.service.admin.service.AdminBaseService;
import com.sudo.service.admin.service.SysRoleService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.util.List;
import java.util.Set;


/**
 * @author 为道日损 2020-04-25 18:33
 */
@Service
public class SysRoleServiceImpl extends AdminBaseService implements SysRoleService {

    @Resource
    private SysRoleMapper sysRoleMapper;

    @Resource
    private SysRoleMenuMapper sysRoleMenuMapper;

    @Resource
    private SysRoleMapperCustom sysRoleMapperCustom;

    @Resource
    private SysRoleButtonMapper sysRoleButtonMapper;

    @Resource
    private SysUserRoleMapper sysUserRoleMapper;
    /**
     * 分页查询所有角色，条件=用户名或角色名
     * @param page 当前页
     * @param pageSize 每页的数量
     * @return list
     */
    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
    @Override
    public PagedGridResult querySysRoleByPage(Integer page , Integer pageSize, String roleCodeOrName) {

        PageHelper.startPage(page, pageSize);
        PageHelper.orderBy("id desc");
        Example example = new Example(SysRole.class);
        if (StringUtils.isNotBlank(roleCodeOrName)) {
            Example.Criteria criteria = example.createCriteria();
            criteria.orLike("roleCode","%"+roleCodeOrName+"%");
            criteria.orLike("roleName","%"+roleCodeOrName+"%");
        }

        List<SysRole> sysRoleList = sysRoleMapper.selectByExample(example);
        return setterPagedGrid(sysRoleList ,page);
    }

    /**
     * 通过roleCode查找角色
     * @param roleCode 角色代码
     * @return SysRole
     */
    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
    @Override
    public SysRole getSysRoleByRoleCode(String roleCode) {
        Example example = new Example(SysRole.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("roleCode", roleCode);
        return sysRoleMapper.selectOneByExample(example);
    }

    /**
     * 修改角色
     * @param sessionUser 当前session用户
     * @param sysRoleBO 角色参数
     * @return 影响行数
     */
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public int updateSysRoleByCode(String sessionUser , SysRoleBO sysRoleBO) {
        //查询原始数据
        SysRole oldSysRole = getSysRoleByRoleCode(sysRoleBO.getRoleCode());

        String now = LocalDateTimeUtil.getStandardDateTimeStr();
        SysRole sysRole = new SysRole();
        BeanUtils.copyProperties(sysRoleBO, sysRole, "roleCode");
        sysRole.setUpdateBy(sessionUser);
        sysRole.setUpdateTime(now);
        boolean isEqual = SysStatus.ON.name().equals(sysRole.getStatus());
        if (!isEqual) {
            sysRole.setStatus(SysStatus.OFF.name());
        }

        Example example = new Example(SysRole.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("roleCode", sysRoleBO.getRoleCode());

        int affectCount = sysRoleMapper.updateByExampleSelective(sysRole, example);

        //====== redis ======
        if (affectCount > 0) {
            //删除 redis_role_permission (可用角色-可用菜单-可用按钮)
            //删除 redis_role_user (可用角色-可用用户)
            if (!oldSysRole.getStatus().equals(sysRoleBO.getStatus())) {
                String roleCode = oldSysRole.getRoleCode();
                if (isEqual) {
                    //on
                    onLoadAllRoleMenuButtonAuth(roleCode,null);
                    onLoadSysRoleUserToRedis(roleCode);
                } else {
                    //off
                    delRedisRolePermissionFromRedis(roleCode);
                    delRedisRoleUserFromRedis(roleCode,null);
                }
            }
        }
        return affectCount;

    }


    /**
     * 获取所有角色的code和name
     * @return list
     */
    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
    @Override
    public List<SysRole> getAllSysRoleCodeAndNameWithoutSuperByStatus(String status) {
        Example example = new Example(SysRole.class);
        example.selectProperties("roleName", "roleCode");
        Example.Criteria criteria = example.createCriteria();
        criteria.andNotEqualTo("roleCode", UserConstant.SUPER_ADMIN);
        if (StringUtils.isNotBlank(status)) {
            criteria.andEqualTo("status", status);
        }
        return sysRoleMapper.selectByExample(example);
    }

    /**
     * 分页获取一个简单角色列表（角色名，代码）
     * @param page 当前行
     * @param pageSize 每页行数
     * @return pageResult
     */
    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
    @Override
    public PagedGridResult getSimpleRoleByPage(Integer page, Integer pageSize,String status) {
        PageHelper.startPage(page, pageSize);
        List<SysRoleVO> sysRoleVOList = sysRoleMapperCustom.getSimpleRole(status);
        return setterPagedGrid(sysRoleVOList,page);
    }

    /**
     * 添加角色
     * @param sysRoleBO 角色参数
     * @param sessionUser 当前session用户
     * @return int
     */
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public int addSysRole(SysRoleBO sysRoleBO,String sessionUser) {
        SysRole sysRole = new SysRole();
        BeanUtils.copyProperties(sysRoleBO, sysRole);
        sysRole.setCreateBy(sessionUser);
        sysRole.setCreateTime(LocalDateTimeUtil.getStandardDateTimeStr());
        return sysRoleMapper.insertSelective(sysRole);
    }

    /**
     *生成roleCode，严于律己，虽然生成相同的roleCode几率小得可怜，
     * 但墨菲定律告诉我们，只要发生一件事的几率存在，哪怕这种概率小得可怜，
     * 总有一天会发生
     */
    private void setRoleCode(SysRole sysRole){
        String roleCode = RandomUtil.getRandomStr(8);
        if (hasRoleCode(roleCode)) {
            setRoleCode(sysRole);
        }else {
            sysRole.setRoleCode(roleCode);
        }
    }

    /**
     * 判断数据库中是否已存在相同的roleCode
     * @param roleCode 角色Code
     * @return boolean
     */
    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
    @Override
    public boolean hasRoleCode(String roleCode){
        Example example = new Example(SysRole.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("roleCode", roleCode);
        List<SysRole> sysRoleList = sysRoleMapper.selectByExample(example);
        return !CollectionUtils.isEmpty(sysRoleList);
    }

    /**
     * 通过roleCode获取角色拥有权限的菜单code
     * @param roleCode 角色code
     * @return list
     */
    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
    @Override
    public List<SysRoleMenu> getMyMenuCodeByRoleCode(String roleCode) {
        return sysRoleMapperCustom.getMyMenuCodeByRoleCode(roleCode);
    }

    /**
     * 禁用（即删除）角色-菜单权限，同时也要禁用（删除）菜单对应的按钮权限
     * @param roleCode 角色code
     * @param menuCode 菜单code
     * @return int
     */
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public int offRoleMenuAuth(String roleCode, String menuCode) {
        Example example1 = new Example(SysRoleMenu.class);
        Example.Criteria criteria1 = example1.createCriteria();
        criteria1.andEqualTo("roleCode", roleCode);
        criteria1.andEqualTo("menuCode", menuCode);

        //删除角色对应的菜单下的按钮权限
        Example example2 = new Example(SysRoleButton.class);
        Example.Criteria criteria2 = example2.createCriteria();
        criteria2.andEqualTo("roleCode", roleCode);
        criteria2.andEqualTo("menuCode", menuCode);
        sysRoleButtonMapper.deleteByExample(example2);

        int affectCount = sysRoleMenuMapper.deleteByExample(example1);

        //====== redis ======
        // redis_role_permission (删除可用角色-可用菜单)
        if (affectCount > 0) {
            delRedisRolePermissionFromRedis(roleCode,menuCode,null);
        }
        return affectCount;
    }

    /**
     * 启用（即新增）角色菜单权限
     * @param roleCode 角色code
     * @param menuCode 菜单code
     * @return int
     */
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public int onRoleMenuAuth(String roleCode, String menuCode) {
        SysRoleMenu sysRoleMenu = new SysRoleMenu();
        sysRoleMenu.setRoleCode(roleCode);
        sysRoleMenu.setMenuCode(menuCode);

        int affectCount = sysRoleMenuMapper.insert(sysRoleMenu);

        //====== redis ======
        // redis_menu_button (新增可用菜单.可用按钮*)
        if (affectCount > 0) {
            //新增redis中角色菜单权限
            List<SysRoleButtonVO> sysRoleButtonVOList = sysRoleMapperCustom.getRoleMenuButtonAuth(roleCode, menuCode);
            if (CollectionUtils.isEmpty(sysRoleButtonVOList)) {
                setRedisRolePermissionToRedis(roleCode, menuCode, UserConstant.NONE);
            } else {
                setRedisRolePermissionToRedis(sysRoleButtonVOList);
            }
        }
        return affectCount;
    }

    /**
     * 禁用（即删除）角色按钮权限
     * @param roleCode 角色
     * @param menuCode 菜单
     * @param buttonCode 按钮
     * @return int
     */
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public int offRoleButtonAuth(String roleCode, String menuCode, String buttonCode) {
        Example example = new Example(SysRoleButton.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("roleCode", roleCode);
        criteria.andEqualTo("menuCode", menuCode);
        criteria.andEqualTo("buttonCode", buttonCode);
        int affectCount = sysRoleButtonMapper.deleteByExample(example);

        //====== redis ======
        // redis_role_permission (删除可用角色-可用菜单)
        if (affectCount > 0) {
            delRedisRolePermissionFromRedis(roleCode, menuCode, buttonCode);
        }
        return affectCount;
    }

    /**
     * 启用（即新增）角色按钮权限
     * @param roleCode 角色
     * @param menuCode 菜单
     * @param buttonCode 按钮
     * @return int
     */
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public int onRoleButtonAuth(String roleCode, String menuCode, String buttonCode) {
        SysRoleButton sysRoleButton = new SysRoleButton();
        sysRoleButton.setButtonCode(buttonCode);
        sysRoleButton.setRoleCode(roleCode);
        sysRoleButton.setMenuCode(menuCode);

        int affectCount = sysRoleButtonMapper.insert(sysRoleButton);
        //====== redis ======
        // redis_role_permission (删除可用角色-可用菜单)
        if (affectCount > 0) {
            //新增redis中角色菜单权限
            setRedisRolePermissionToRedis(roleCode,menuCode,buttonCode);
        }
        return affectCount;
    }

    /**
     * 通过用户名获取角色
     * @param username 用户名
     * @return sysRole
     */
    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
    @Override
    public SysRole getRoleByUsername(String username) {
        Example example = new Example(SysUserRole.class);
        example.selectProperties("roleCode");
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("username",username);
        SysUserRole sysUserRole = sysUserRoleMapper.selectOneByExample(example);

        Example example1 = new Example(SysRole.class);
        Example.Criteria criteria1 = example1.createCriteria();
        criteria1.andEqualTo("roleCode",sysUserRole.getRoleCode());
        return sysRoleMapper.selectOneByExample(example1);
    }

    /**
     * 通过用户名查找对用的角色Code
     * @param username 用户名
     * @return roleCode
     */
    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
    @Override
    public String getRoleCodeByUsername(String username) {

        Example example = new Example(SysUserRole.class);
        example.selectProperties("roleCode");
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("username", username);
        SysUserRole sysUserRole = sysUserRoleMapper.selectOneByExample(example);
        return sysUserRole.getRoleCode();
    }

    /**
     *通过角色代码查找对应的用户数量
     * @param roleCode 角色Code
     * @return 用户数
     */
    @Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
    @Override
    public int getUserCountByRoleCode(String roleCode) {
        Example example = new Example(SysUserRole.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("roleCode", roleCode);
        return sysUserRoleMapper.selectCountByExample(example);
    }

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public int deleteSysRoleByRoleCode(String roleCode) {
        Example example = new Example(SysRole.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("roleCode", roleCode);

        //删除角色-菜单
        Example example1 = new Example(SysRoleMenu.class);
        Example.Criteria criteria1 = example1.createCriteria();
        criteria1.andEqualTo("roleCode", roleCode);
        sysRoleMenuMapper.deleteByExample(example1);

        //删除角色-按钮
        Example example2 = new Example(SysRoleButton.class);
        Example.Criteria criteria2 = example2.createCriteria();
        criteria2.andEqualTo("roleCode", roleCode);
        sysRoleButtonMapper.deleteByExample(example2);

        int affectCount = sysRoleMapper.deleteByExample(example);

        // ====== redis ======
        if (affectCount > 0) {
            // redis_role_user (删除可用角色-可用用户)
            // redis_role_permission (删除可用角色-可用菜单-可用按钮)
            delRedisRoleUserFromRedis(roleCode, null);
            delRedisRolePermissionFromRedis(roleCode);
        }
        return affectCount;
    }


    @Override
    public Set<String> getRolePermissionFromRedis(String username, String menuCode) {
        String roleCode = null;
        Set<String> keys = stringRedisUtil.keys(redisKeys.redisRoleUser("*"));
        for (String key : keys) {
            boolean flag = stringRedisUtil.sIsMember(key, username);
            if (flag) {
                roleCode = key.replace(RedisKeys.REDIS_ROLE_USER+":","");
                break;
            }
        }
        return stringRedisUtil.getMembers(redisKeys.redisRolePermission(roleCode,menuCode));
    }

    @Override
    public Set<String> getMenuPermissionFromRedis(String menuCode) {
        return stringRedisUtil.getMembers(redisKeys.redisMenuButton(menuCode));
    }

    @Override
    public List<SysRole> getAllSysRoleWithoutSuper() {
        Example example = new Example(SysRole.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andNotEqualTo("roleCode", UserConstant.SUPER_ADMIN);
        return sysRoleMapper.selectByExample(example);
    }
}
